BRUSSELATOR
Overview
The BRUSSELATOR function numerically solves the Brusselator system of ordinary differential equations, a theoretical model for autocatalytic chemical reactions that can exhibit sustained oscillations. The Brusselator (a portmanteau of “Brussels” and “oscillator”) was proposed by Nobel laureate Ilya Prigogine and collaborators at the Université Libre de Bruxelles in 1968.
The model describes the dynamics of two chemical species, X and Y, whose concentrations evolve according to the following system of differential equations:
\frac{dX}{dt} = a - (b + 1)X + X^2 Y
\frac{dY}{dt} = bX - X^2 Y
where a and b are positive rate constants. The system has a fixed point at (X, Y) = (a, b/a). The stability of this fixed point depends on the parameter values: when b > 1 + a^2, the fixed point becomes unstable and the system exhibits self-sustained oscillations approaching a limit cycle. Unlike the Lotka-Volterra equations, these oscillations are independent of initial conditions.
The Brusselator is closely related to real chemical oscillators such as the Belousov-Zhabotinsky reaction, a famous example of a clock reaction that exhibits visually striking color oscillations.
This implementation uses SciPy’s solve_ivp function to integrate the ODE system. The default Runge-Kutta method (RK45) works well for most cases, but alternative methods including RK23, DOP853, Radau, BDF, and LSODA are available for different accuracy requirements or stiff systems.
This example function is provided as-is without any representation of accuracy.
Excel Usage
=BRUSSELATOR(x_initial, y_initial, a, b, t_start, t_end, timesteps, solve_ivp_method)
x_initial(float, required): Initial concentration of species X.y_initial(float, required): Initial concentration of species Y.a(float, required): Reaction rate parameter a (positive constant).b(float, required): Reaction rate parameter b (positive constant).t_start(float, required): Start time for integration.t_end(float, required): End time for integration.timesteps(int, optional, default: 10): Number of timesteps to solve for.solve_ivp_method(str, optional, default: “RK45”): Integration method for solving the ODE system.
Returns (list[list]): 2D list with header [t, X, Y], or error message string.
Examples
Example 1: Demo case 1
Inputs:
| x_initial | y_initial | a | b | t_start | t_end |
|---|---|---|---|---|---|
| 1 | 1 | 1 | 2 | 0 | 1 |
Excel formula:
=BRUSSELATOR(1, 1, 1, 2, 0, 1)
Expected output:
| t | X | Y |
|---|---|---|
| 0 | 1 | 1 |
| 0.111 | 0.9002 | 1.106 |
| 0.222 | 0.8197 | 1.201 |
| 0.333 | 0.7544 | 1.29 |
| 0.444 | 0.7008 | 1.374 |
| 0.556 | 0.6566 | 1.454 |
| 0.667 | 0.6205 | 1.531 |
| 0.778 | 0.5921 | 1.603 |
| 0.889 | 0.5709 | 1.671 |
| 1 | 0.5544 | 1.736 |
Example 2: Demo case 2
Inputs:
| x_initial | y_initial | a | b | t_start | t_end | timesteps |
|---|---|---|---|---|---|---|
| 2 | 1 | 2 | 3 | 0 | 1 | 10 |
Excel formula:
=BRUSSELATOR(2, 1, 2, 3, 0, 1, 10)
Expected output:
| t | X | Y |
|---|---|---|
| 0 | 2 | 1 |
| 0.111 | 1.818 | 1.192 |
| 0.222 | 1.695 | 1.343 |
| 0.333 | 1.609 | 1.468 |
| 0.444 | 1.551 | 1.573 |
| 0.556 | 1.514 | 1.662 |
| 0.667 | 1.495 | 1.736 |
| 0.778 | 1.491 | 1.796 |
| 0.889 | 1.501 | 1.843 |
| 1 | 1.524 | 1.874 |
Example 3: Demo case 3
Inputs:
| x_initial | y_initial | a | b | t_start | t_end | timesteps |
|---|---|---|---|---|---|---|
| 1 | 2 | 1 | 2 | 0 | 2 | 10 |
Excel formula:
=BRUSSELATOR(1, 2, 1, 2, 0, 2, 10)
Expected output:
| t | X | Y |
|---|---|---|
| 0 | 1 | 2 |
| 0.222 | 1 | 2 |
| 0.444 | 1 | 2 |
| 0.667 | 1 | 2 |
| 0.889 | 1 | 2 |
| 1.111 | 1 | 2 |
| 1.333 | 1 | 2 |
| 1.556 | 1 | 2 |
| 1.778 | 1 | 2 |
| 2 | 1 | 2 |
Example 4: Demo case 4
Inputs:
| x_initial | y_initial | a | b | t_start | t_end | timesteps | solve_ivp_method |
|---|---|---|---|---|---|---|---|
| 1 | 1 | 1 | 2 | 0 | 1 | 10 | RK23 |
Excel formula:
=BRUSSELATOR(1, 1, 1, 2, 0, 1, 10, "RK23")
Expected output:
| t | X | Y |
|---|---|---|
| 0 | 1 | 1 |
| 0.111 | 0.9001 | 1.106 |
| 0.222 | 0.8195 | 1.202 |
| 0.333 | 0.754 | 1.291 |
| 0.444 | 0.701 | 1.374 |
| 0.556 | 0.658 | 1.453 |
| 0.667 | 0.6234 | 1.527 |
| 0.778 | 0.5959 | 1.598 |
| 0.889 | 0.574 | 1.666 |
| 1 | 0.557 | 1.731 |
Python Code
from scipy.integrate import solve_ivp as scipy_solve_ivp
import numpy as np
def brusselator(x_initial, y_initial, a, b, t_start, t_end, timesteps=10, solve_ivp_method='RK45'):
"""
Numerically solves the Brusselator system of ordinary differential equations for autocatalytic chemical reactions.
See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.solve_ivp.html
This example function is provided as-is without any representation of accuracy.
Args:
x_initial (float): Initial concentration of species X.
y_initial (float): Initial concentration of species Y.
a (float): Reaction rate parameter a (positive constant).
b (float): Reaction rate parameter b (positive constant).
t_start (float): Start time for integration.
t_end (float): End time for integration.
timesteps (int, optional): Number of timesteps to solve for. Default is 10.
solve_ivp_method (str, optional): Integration method for solving the ODE system. Valid options: RK45, RK23, DOP853, Radau, BDF, LSODA. Default is 'RK45'.
Returns:
list[list]: 2D list with header [t, X, Y], or error message string.
"""
# Define valid methods
valid_methods = ['RK45', 'RK23', 'DOP853', 'Radau', 'BDF', 'LSODA']
# Validate input types
try:
x0 = float(x_initial)
y0 = float(y_initial)
param_a = float(a)
param_b = float(b)
t0 = float(t_start)
t1 = float(t_end)
num_timesteps = int(timesteps)
except Exception:
return "Invalid input: All initial values, parameters, and timesteps must be numbers."
if t1 <= t0:
return "Invalid input: t_end must be greater than t_start."
if num_timesteps <= 0:
return "Invalid input: timesteps must be a positive integer."
if solve_ivp_method not in valid_methods:
return f"Invalid input: solve_ivp_method must be one of {valid_methods}."
# Create time array for evaluation
t_eval = np.linspace(t0, t1, num_timesteps)
# Brusselator ODE system (defined inside main function per guidelines)
def brusselator_ode(t, state):
x, y = state
dxdt = param_a - (param_b + 1) * x + x * x * y
dydt = param_b * x - x * x * y
return [dxdt, dydt]
# Integrate
try:
sol = scipy_solve_ivp(
brusselator_ode,
[t0, t1],
[x0, y0],
method=solve_ivp_method,
dense_output=False,
t_eval=t_eval
)
except Exception as e:
return f"Integration error: {e}"
if not sol.success:
return f"Integration failed: {sol.message}"
# Helper function to check for valid numeric values
def is_valid_number(val):
return np.isfinite(val)
# Format output: header row then data rows
result = [["t", "X", "Y"]]
for i in range(len(sol.t)):
t_val = float(sol.t[i])
x_val = float(sol.y[0][i])
y_val = float(sol.y[1][i])
# Disallow nan/inf
if not all(is_valid_number(v) for v in [t_val, x_val, y_val]):
return "Invalid output: nan or inf detected."
result.append([t_val, x_val, y_val])
return result